/*
 *  Property.cpp
 *  IncrementalAlgorithmSerial
 *
 *  Created by Roman Kutlak on 29/03/2010.
 *  Copyright 2010 Roman Kutlak. All rights reserved.
 *
 */
#include <sstream>

#include "Property.h"
#include "Utilities.h"

using namespace std;

namespace kutlak {
namespace roman {
namespace gre {
    
    
    
#define PROPERTY_DEBUG 0
    
    //--------------------------------------------------------------------------
#pragma mark -
#pragma mark lifecycle            
    
    Property::Property(const Vindex_t& inAttribute, const Vindex_t& inValue) :
    mAttribute(inAttribute), mValue(inValue),
    fNegation(false), fInverse(false) {}
    
    Property::Property(const string& inAttribute, const string& inValue) :
    mAttribute(0), mValue(0),
    fNegation(false), fInverse(false) 
    {
        Init(inAttribute, inValue);
    }
    
    Property::Property(const Vindex_t& inAttribute, const string& inValue) :
    mAttribute(inAttribute), mValue(0),
    fNegation(false), fInverse(false) 
    {
        mValue = Voc::Instance().Index(inValue);
    }
    
    Property::Property(const Property& src) :
    mAttribute(src.mAttribute),
    mValue(src.mValue),
    fNegation(src.fNegation),
    fInverse(src.fInverse) {}
    
    Property::Property(Property&& src) :
    mAttribute(std::move(src.mAttribute)),
    mValue(std::move(src.mValue)),
    fNegation(std::move(src.fNegation)),
    fInverse(std::move(src.fInverse)) {}
    
    Property& Property::operator=(Property rhs) noexcept(true) 
    {
        swap(rhs, *this);
        return *this;
    }
    
    void swap(Property& first, Property& second) noexcept(true) 
    {
        using std::swap;
        
        swap(first.mAttribute, second.mAttribute);
        swap(first.mValue, second.mValue);
        swap(first.fNegation, second.fNegation);
        swap(first.fInverse, second.fInverse);
    }
    
    Property::~Property() noexcept(true) = default;
    
    //--------------------------------------------------------------------------
#pragma mark -
#pragma mark operators
    
    
    bool Property::operator==(const Property& rhs) const 
    {
        return (mValue == rhs.mValue &&
                mAttribute == rhs.mAttribute &&
                fNegation == rhs.fNegation &&
                fInverse == rhs.fInverse);
    }
    
    bool Property::operator!=(const Property& rhs) const 
    {
        return !(rhs == *this);
    }
    
    bool Property::operator<(const Property& rhs) const 
    {
        if (mAttribute == rhs.mAttribute)
            return (mValue < rhs.mValue);
        
        return (mAttribute < rhs.mAttribute);
    }
    
    bool Property::operator>(const Property& rhs) const 
    {
        return !(*this < rhs);
    }
    //--------------------------------------------------------------------------
#pragma mark -
#pragma mark getters
    
    const Vindex_t& Property::Attribute() const 
    {
        return mAttribute;
    }
    
    string Property::AttributeStr() const
    {
        return Voc::Instance().String(mAttribute);
    }
    
    const Vindex_t& Property::Value() const 
    {
        return mValue;
    }
    
    string Property::ValueStr() const
    {
        return Voc::Instance().String(mValue);
    }
    
    bool Property::Negation() const 
    {
        return fNegation;
    }
    
    bool Property::Inverse() const 
    {
        return fInverse;
    }
    //--------------------------------------------------------------------------
#pragma mark -
#pragma mark setters
    
    void Property::SetAttribute(const Vindex_t& att) 
    {
        mAttribute = att;
    }
    
    void Property::SetValue(const Vindex_t& val) 
    {
        mValue = val;
    }
    
    void Property::SetAttribute(const string& att) 
    {
        mAttribute = Voc::Instance().Index(att);
    }
    
    void Property::SetValue(const string& val) 
    {
        mValue = Voc::Instance().Index(val);
    }
    
    void Property::SetAttribute(string&& att) 
    {
        mAttribute = Voc::Instance().Index(std::move(att));
    }
    
    void Property::SetValue(string&& val) 
    {
        mValue = Voc::Instance().Index(std::move(val));
    }
    
    void Property::SetNegation(bool flag) 
    {
        fNegation = flag;
    }
    
    void Property::SetInverse(bool flag) 
    {
        fInverse = flag;
    }
    
    
    //--------------------------------------------------------------------------
#pragma mark -
#pragma mark I/O
    
    ostream& operator<<(ostream& os, const Property& obj) 
    {
        obj.Serialise(os);
        return os;
    }
    
    istream& operator>>(istream& is, Property& obj) 
    {
        Property tmp;
        tmp.Deserialise(is);
        swap(obj, tmp);
        
        return is;
    }
    
    void Property::Serialise(ostream& os) const 
    {
        os << "<p> ";
        os << mAttribute << ' ';
        os << mValue << ' ';
        os << fNegation << ' ';
        os << fInverse << ' ';
        os << "</p>\n";
    }
    
    void Property::Deserialise(istream& is) 
    {
        string tmp;
        if (! (is >> tmp) || "<p>" != tmp) 
        {
            string message = "Expected <p>.";
            throw DeserialisationException(std::move(message),
                                           std::move(tmp));
        }
        
        if (!(is >> mAttribute >> mValue >> fNegation >> fInverse)) 
        {
            string message = "Could not read data.";
            throw DeserialisationException(std::move(message));
        }
        
        if (!(is >> tmp) || "</p>" != tmp) 
        {
            string message = "Expected </p>.";
            throw DeserialisationException(std::move(message),
                                           std::move(tmp));
        }
    }
    
    
    ostream& Property::ToRdf(ostream& os) const 
    {
        Voc& vocab = Voc::Instance();
        
        os << "<" << vocab.String(mAttribute) << ">";
        os << " ";
        // TODO: the <> characters only apply when a value is not a literal.
        os << "<" << vocab.String(mValue) << ">";
        
        return os;
    }
    
    string Property::ToRdf() const 
    {
        stringstream res;
        ToRdf(res);
        
        return res.str();
    }
    
    istream& Property::FromRdf(istream& is) 
    {
        // property is encoded as <attribute> <value>
        string attribute;
        string value;
        if (!(is >> attribute &&
              getline(is, value))) 
        {
            const string message = "Failed to read property.";
            throw RdfException(message);
        }
        
        attribute = Trim(attribute);
        value = Trim(value);
        
        if (attribute.front() == '<')
            attribute = attribute.substr(1);
        
        if (attribute.back() == '>')
            attribute.pop_back();
        
        if (value.front() == '<')
            value = value.substr(1);
        
        if (value.back() == '>')
            value.pop_back();
        
        SetAttribute(attribute);
        SetValue(value);
        return is;
    }
    
    string Property::ToString() const 
    {
        string str = "";
        
        str += "Property ";
        
        if (fNegation)
            str += "NEG ";
        
        if (fInverse)
            str += "INV ";
        
        if (mAttribute == DEFAULT_ATTRIBUTE)
            str += "No Attribute";
        else
            str += Voc::Instance().String(mAttribute);
        
        str += " ";
        
        if (mValue == DEFAULT_VALUE)
            str += "No Value";
        else
            str += Voc::Instance().String(mValue);
        
        return str;
    }
    
    //////// private ///////////
    
    void Property::Init(const string& inAttribute, const string& inValue) 
    {
        Voc& vocab = Voc::Instance();
        mAttribute = vocab.Index(inAttribute);
        mValue = vocab.Index(inValue);     
    }
    
    size_t Property::Hash() const 
    {
        std::hash<Vindex_t> hasher;
        size_t attrHash = hasher(mAttribute);
        size_t valHash = hasher(mValue);
        return attrHash ^ (valHash << 1);
    }
    
} // namespace gre
} // namespace roman
} // namespace kutlak


